/*
 * byte_stream.h
 *
 *  Created on: 2008-12-5
 *      Author: fanliangliang
 */

#ifndef BYTE_STREAM_H_
#define BYTE_STREAM_H_

#include "bits/config.h"
#include <stdint.h>
#include <vector>
#include <string>
#include <ios>

BEGIN_NAMESPACE_TOXIC

using std::vector;
using std::string;
using std::ios_base;

enum ByteOrder {
	BIG_ENDIAN_, LITTLE_ENDIAN_
};

class Network {
public:
	static uint16_t htol(uint16_t i) { return helper().htol16(i); }
	static uint32_t htol(uint32_t i) { return helper().htol32(i); }
	static uint64_t htol(uint64_t i) { return helper().htol64(i); }
	static float htol(float f) { return helper().htolf(f); }
	static double htol(double d) { return helper().htold(d); }

	static uint16_t htob(uint16_t i) { return helper().htob16(i); }
	static uint32_t htob(uint32_t i) { return helper().htob32(i); }
	static uint64_t htob(uint64_t i) { return helper().htob64(i); }
	static float htob(float f) { return helper().htobf(f); }
	static double htob(double d) { return helper().htobd(d); }

	static uint16_t ltoh(uint16_t i) { return helper().ltoh16(i); }
	static uint32_t ltoh(uint32_t i) { return helper().ltoh32(i); }
	static uint64_t ltoh(uint64_t i) { return helper().ltoh64(i); }
	static float ltoh(float f) { return helper().ltohf(f); }
	static double ltoh(double d) { return helper().ltohd(d); }

	static uint16_t btoh(uint16_t i) { return helper().btoh16(i); }
	static uint32_t btoh(uint32_t i) { return helper().btoh32(i); }
	static uint64_t btoh(uint64_t i) { return helper().btoh64(i); }
	static float btoh(float f) { return helper().btohf(f); }
	static double btoh(double d) { return helper().btohd(d); }

	ByteOrder byteOrder() const { return helper().byteOrder; }

private:
	struct Helper {
		Helper();
		~Helper();

		uint16_t (*htol16)(uint16_t i);
		uint32_t (*htol32)(uint32_t i);
		uint64_t (*htol64)(uint64_t i);
		float (*htolf)(float f);
		double (*htold)(double d);

		uint16_t (*htob16)(uint16_t i);
		uint32_t (*htob32)(uint32_t i);
		uint64_t (*htob64)(uint64_t i);
		float (*htobf)(float f);
		double (*htobd)(double d);

		uint16_t (*ltoh16)(uint16_t i);
		uint32_t (*ltoh32)(uint32_t i);
		uint64_t (*ltoh64)(uint64_t i);
		float (*ltohf)(float f);
		double (*ltohd)(double d);

		uint16_t (*btoh16)(uint16_t i);
		uint32_t (*btoh32)(uint32_t i);
		uint64_t (*btoh64)(uint64_t i);
		float (*btohf)(float f);
		double (*btohd)(double d);

		ByteOrder byteOrder;
	};

	static Helper& helper();
};


class ByteStream {
public:
	ByteStream(ByteOrder byteOrder = BIG_ENDIAN_);
	ByteStream(const void* buffer, size_t length, ByteOrder byteOrder = BIG_ENDIAN_);
	virtual ~ByteStream();

	template <typename T>
	ByteStream& operator>>(T& value) {
		if (read_aux(&value, sizeof(T))) {
			value = etoh(value);
		}
		return *this;
	}

	ByteStream& operator>>(uint8_t& i);
	ByteStream& operator>>(int8_t& i);
	ByteStream& operator>>(int16_t& i);
	ByteStream& operator>>(int32_t& i);
	ByteStream& operator>>(int64_t& i);
	ByteStream& operator>>(bool& b);
	ByteStream& operator>>(char*& s);
	ByteStream& operator>>(string& s);

	template <typename T>
	ByteStream& operator<<(T value) {
		value = htoe(value);
		return write(&value, sizeof(T));
	}

	ByteStream& operator<<(uint8_t i);
	ByteStream& operator<<(int8_t i);
	ByteStream& operator<<(int16_t i);
	ByteStream& operator<<(int32_t i);
	ByteStream& operator<<(int64_t i);
	ByteStream& operator<<(bool b);
	ByteStream& operator<<(const char* s);
	ByteStream& operator<<(const string& s);

	ByteStream& read(void* buffer, size_t length);
	ByteStream& write(const void* buffer, size_t length);
	ByteStream& seekg(int pos, ios_base::seekdir dir = ios_base::cur);
	ByteStream& seekp(int pos, ios_base::seekdir dir = ios_base::cur);

	const byte* data() const;
	operator void*() const;
	bool operator !() const;
	bool eof() const;
	size_t gcount() const;
	size_t size() const;
	size_t tellg() const;
	size_t tellp() const;

	void dump(const char* filename) const;

private:
	bool read_aux(void* buffer, size_t length);
	void seek_aux(size_t& seekpos, int pos, ios_base::seekdir dir);
	void seek_cur(size_t& seekpos, size_t curpos, int pos);

	template <typename T>
	T htoe(T value) {
		return (_byteOrder == LITTLE_ENDIAN_) ? Network::htol(value) : Network::htob(value);
	}

	template <typename T>
	T etoh(T value) {
		return (_byteOrder == LITTLE_ENDIAN_) ? Network::ltoh(value) : Network::btoh(value);
	}

private:
	vector<byte> _data;
	size_t _gcount;
	size_t _gpos;
	size_t _ppos;
	ByteOrder _byteOrder;
};

END_NAMESPACE_TOXIC

#endif /* BYTE_STREAM_H_ */
